/*
	URL: $HeadURL: http://screwplugs.googlecode.com/svn/trunk/src/ScrewPlugs/PathHandler.cs $
	Revision: $Revision: 4 $
	Last modified: $Date: 2010-01-07 15:03:31 +0000 (Thu, 07 Jan 2010) $
	Last changed by: $LastChangedBy: slav.isv $
	
	Author: ornus
	Created on: 2009-12-31 20:31:07
	
	Copyright (c) Soltide, LLC
	This file is part of the ScrewPlugs project
*/
using System.Text.RegularExpressions;
using ScrewTurn.Wiki;
using ScrewTurn.Wiki.PluginFramework;

namespace ScrewPlugs
{
	/// <summary>
	/// Formats page text fixing the path.
	/// </summary>
	public class PathHandler
	{
		/// <summary>
		/// Separator in the path.
		/// </summary>
		private const char PathSeparator = '/';
		/// <summary>
		/// Separator in the wiki.
		/// </summary>
		private readonly char _wikiSeparator = '-';
		/// <summary>
		/// Regular expression to split page text into sub-strings by the link.
		/// </summary>
		private readonly Regex _linkRegex;

		private readonly Regex _imageRegex;

		/// <summary>
		/// Regular expression to split link into components.
		/// </summary>
		private readonly Regex _linkComponentsRegex;

		/// <summary>
		/// Regular expression to split link into parts.
		/// </summary>
		private readonly Regex _linkPartsRegex;


		/// <summary>
		/// Initializes a new instance of the <see cref="PathHandler"/> class.
		/// </summary>
		/// <param name="wikiSeparator">The wiki separator.</param>
		public PathHandler( char wikiSeparator ) : this()
		{
			this._wikiSeparator = wikiSeparator;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="PathHandler"/> class.
		/// </summary>
		public PathHandler( )
		{
			const RegexOptions options = RegexOptions.Compiled;
			_linkRegex = new Regex( @"(\[[^:@\]\n\r]*\])|({t:[^:@}]*})", options | RegexOptions.Multiline );
			
			this._imageRegex = new Regex( @"(\[image(?:left|right|auto)?\|[^\]]*\])", options | RegexOptions.Multiline );

			this._linkComponentsRegex = new Regex( @"({t:|[\[\]\|])", options );

			this._linkPartsRegex = new Regex( this._wikiSeparator + @"?(\.\.|\.(?=$|" + this._wikiSeparator + "))", options );
		}

		/// <summary>
		/// Evaluates the path.
		/// </summary>
		/// <param name="pageText">The page text.</param>
		/// <param name="pageName">The page name.</param>
		/// <returns>Page text with path string correctly formatted.</returns>
		public string EvaluatePath( string pageText, string pageName )
		{
			string[] pageParts = SplitPage( pageText );
			for( int i = 0; i < pageParts.Length; i++ )
			{
				if( pageParts[ i ].Length == 0 ) continue;
				this.HandlePath( ref pageParts[ i ], pageName );
			}
			return string.Concat( pageParts );
		}

		/// <summary>
		/// Handles the path.
		/// </summary>
		/// <param name="part">The part.</param>
		/// <param name="pageName">The page name.</param>
		private void HandlePath( ref string part, string pageName )
		{
			if( IsWikiLink( part ) && !SkipLinkProcessing( part ))
			{
				var result = FixSeparator( part );
				var linkComponents = this.SplitLinkIntoComponents( result );
				if( linkComponents.Length >= 3 )
				{
					linkComponents[ 2 ] = FixPath( linkComponents[ 2 ], pageName );
				}
				part = string.Concat( linkComponents );
			}
		}

		/// <summary>
		/// Fixes the path.
		/// </summary>
		/// <param name="link">The link.</param>
		/// <param name="pageName">The page name.</param>
		/// <returns>Link with . and .. fixed.</returns>
		private string FixPath( string link, string pageName )
		{
			if( link.Length > 0 && link[ 0 ] == this._wikiSeparator )
			{
				link = string.Concat( pageName, link );
			}
			var linkParts = SplitLinkIntoParts( link );

			var fixedLink = pageName;
			var firstPart = true;
			foreach( var part in linkParts )
			{
				if( part.Length == 0 ) continue;
				fixedLink = FixLinkPart( part, firstPart, fixedLink );
				firstPart = false;
			}
			return fixedLink;
		}

		/// <summary>
		/// Fixes the link part.
		/// </summary>
		/// <param name="part">The part.</param>
		/// <param name="firstPart"><c>true</c> if <paramref name="part"/> is the 1st part.</param>
		/// <param name="pageName">Page name relative to which path part is.</param>
		/// <returns>Page name based on the part.</returns>
		private string FixLinkPart( string part, bool firstPart, string pageName )
		{
			switch( part )
			{
				case  ".":
					Log.LogEntry( "page name = " + pageName, EntryType.General, "Path Handler" ); 
					return pageName;
				case "..":
					int lastSeparator = pageName.LastIndexOf( this._wikiSeparator );
					return lastSeparator >= 0 ? pageName.Substring( 0, lastSeparator ) : string.Empty;
				default:
					if( firstPart || pageName.Length == 0 )
					{
						return part;
					}
					else
					{
						return string.Concat( pageName, this._wikiSeparator, part );
					}
			}
		}

		/// <summary>
		/// Splits the link into parts.
		/// </summary>
		/// <param name="link">The link.</param>
		/// <returns><b>Page1-Page3-..-Page2</b> results in <c>{ "Page1-Page3", "..", "Page2" }</c>.</returns>
		private string[] SplitLinkIntoParts( string link )
		{
			string[] parts = this._linkPartsRegex.Split( link );
			for( int i = 0; i < parts.Length; i++ )
			{
				parts[ i ] = parts[ i ].Trim( this._wikiSeparator );
			}
			return parts;
		}

		/// <summary>
		/// Splits the link.
		/// </summary>
		/// <param name="result">The result.</param>
		/// <returns>{ "[", link , "|", description, "]" } or something similar.</returns>
		private string[] SplitLinkIntoComponents( string result )
		{
			return this._linkComponentsRegex.Split( result );
		}

		/// <summary>
		/// Determines whether the specified part is a wiki link.
		/// </summary>
		/// <param name="part">The part.</param>
		/// <returns>
		/// 	<c>true</c> if the specified part is a wiki link; otherwise, <c>false</c>.
		/// </returns>
		private bool IsWikiLink( string part )
		{
			return _linkRegex.IsMatch( part );
		}

		private bool SkipLinkProcessing( string link )
		{
			return _imageRegex.IsMatch( link );
		}

		/// <summary>
		/// Splits the page.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <returns>Page split into string by the links on the page.</returns>
		private string[] SplitPage( string text )
		{
			return this._linkRegex.Split( text );
		}


		/// <summary>
		/// Fixes the separator.
		/// </summary>
		/// <param name="text">The text.</param>
		/// <returns>Path with separators fixed.</returns>
		private string FixSeparator( string text )
		{
			return text.Replace( PathSeparator, _wikiSeparator );
		}
	}
}
